package com.apes.scm.invoice.service;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.plugin.esb.api.SimpleRequest;
import com.apes.framework.util.*;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.repository.CooperationRepository;
import com.apes.scm.account.ledger.service.LedgerService;
import com.apes.scm.account.tax.repository.TaxRepository;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.contract.repository.ContractRepository;
import com.apes.scm.income.model.Income;
import com.apes.scm.income.model.IncomeItem;
import com.apes.scm.income.repository.IncomeRepository;
import com.apes.scm.invoice.model.Invoice;
import com.apes.scm.invoice.model.InvoiceItem;
import com.apes.scm.invoice.repository.InvoiceItemRepository;
import com.apes.scm.invoice.repository.InvoiceRepository;
import com.apes.scm.invoice.repository.InvoiceltemDao;
import com.apes.scm.masterdata.enumeration.repository.EnumerationRepository;
import com.apes.scm.masterdata.finance.repository.IncomeTypeRepository;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.party.model.Party;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.masterdata.product.model.Product;
import com.apes.scm.settle.settle.model.Settle;
import com.apes.scm.settle.settle.model.SettleItem;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherItemRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apestech.framework.json.JSON;
import com.google.common.base.Strings;
import org.hibernate.query.internal.NativeQueryImpl;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneId;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 功能：账单服务
 *
 * @author xul
 * @create 2018-03-13 19:13
 */
@Service("invoiceService")
public class InvoiceService extends DomainService {

    @Autowired
    InvoiceRepository invoiceRepository;

    @Autowired
    InvoiceItemRepository invoiceItemRepository;

    @Autowired
    LedgerService ledgerService;

    @Autowired
    PartyRoleRepository partyRoleRepository;

    @Autowired
    private InvoiceltemDao invoiceltemDao;

    @Autowired
    private DeptRepository deptRepository;

    @Autowired
    private CooperationRepository cooperationRepository;

    @Autowired
    private EnumerationRepository enumerationRepository;

    @Autowired
    private EntityManager entityManager;

    /**
     * 功能：创建账单
     */
    public Invoice create(SimpleRequest request) {
        Invoice invoice = request.getO(Invoice.class);
        return save(invoice);
    }

    /**
     * 测试
     *
     * @param request
     * @return
     */
    public Invoice test(SimpleRequest request) {
        Invoice invoice = invoiceRepository.getOne(Long.valueOf(request.get("id").toString()));
        return save(invoice);
    }

    public Invoice save(Invoice invoice) {
        invoice.computeAmt();
        if (invoice.getDateAccount() == null) invoice.setDateAccount(new Date());
        if (invoice.getThirdTime() == null) invoice.setThirdTime(new Date());

        operatorProcess(invoice);  // 运营商处理

        //卡内 业务凭证 上游单据等于 外协工单
        JSONArray jsonArray = invoiceltemDao.findServicePlan(invoice.getOrigin());
        if (jsonArray.size() > 0 && "师人订制".equals(((JSONObject) jsonArray.get(0)).get("CHANNEL_NAME"))) {
            invoice = canet(invoice);
        }

        invoice = invoiceRepository.saveAndFlush(invoice);
        ledgerService.charge(invoice.getCompany(), invoice.getDept(), invoice.getPartner(), invoice.getInventoryMode(),
                invoice.getAccountingMethod(), invoice.getInnerBusinessKey(), invoice.getInnerBusinessType(),
                invoice.getDateAccount(), "current_unsettled", invoice.getAmtTotal());
        return invoice;
    }

    /**
     * 卡内生成供应商账单
     *
     * @param invoice
     * @return
     */
    private Invoice canet(Invoice invoice) {
        Invoice canetInvoice = canetInvoice(invoice);//卡内供应商账单
        //生成营运商账单
        PartyRole operator = getOperatorByParty(invoice.getCompany().getParty());
        if (Objects.nonNull(operator)) {
            canetOperator(canetInvoice, operator);
        }
        return canetInvoice;
    }

    private Invoice canetInvoice(Invoice invoice) {
        Invoice invoiceCanet = new Invoice();
        invoiceCanet.setCompany(invoice.getCompany());//公司编码
        invoiceCanet.setPartner(invoice.getPartner());//合作伙伴编码
        invoiceCanet.setDept(invoice.getDept());//合作伙伴编码
        invoiceCanet.setInventoryMode(invoice.getInventoryMode());//库存方式
        invoiceCanet.setContract(invoice.getContract());//合同编码
        invoiceCanet.setAccountingMethod(invoice.getAccountingMethod());//采购核算方式
        invoiceCanet.setSource(invoice.getSource());//源单据号码
        invoiceCanet.setSourceBusinessType(invoice.getSourceBusinessType());//源单据号码
        invoiceCanet.setOrigin(invoice.getOrigin());//源单据号码
        invoiceCanet.setOriginBusinessType(invoice.getOriginBusinessType());//源单据类型
        invoiceCanet.setCustomerExpand(invoice.getCustomerExpand());//会员扩展信息编码
        invoiceCanet.setCreator(invoice.getCreator());//创建人
        invoiceCanet.setCreateDate(new Date());//创建日期
        invoiceCanet.setClassification(invoice.getClassification());//凭证分类
        invoiceCanet.setThirdId(invoice.getThirdId());//第三方单号
        Set<InvoiceItem> invoiceItems = new HashSet();
        invoice.getInvoiceItems().stream().forEach(v -> {
            InvoiceItem invoiceItem = new InvoiceItem();
            VoucherItem voucherItem = SpringManager.getBean(VoucherItemRepository.class).getOne(Long.valueOf(v.getSourceItemId()));
            if (!StringUtils.isEmpty(voucherItem.getSourceItemId())) {
                JSONArray source = invoiceltemDao.maintenanceOrder(voucherItem.getSourceItemId());
                if (source.size() != 0) {
                    JSONObject object = (JSONObject) source.get(0);
                    invoiceItem.setInvoice(invoiceCanet);
                    invoiceItem.setQty(Double.parseDouble(object.get("SALE_QTY").toString()));//数量 = 外协工单 数量
                    invoiceItem.setPriceUnit(Double.parseDouble(object.get("PLATFORM_EX").toString()));//单价 = 外协工单 报价(裸价)
                    double priceSubtotal = Double.parseDouble(object.get("PFT_PRICE_UNIT").toString()) * Double.parseDouble(object.get("SALE_QTY").toString());
                    invoiceItem.setPriceSubtotal(priceSubtotal);//金额
                    invoiceItem.setFloorPrice(Double.parseDouble(object.get("PLATFORM_TAX_TEMP").toString()));//底价 = 工单的’平台成本价‘
                    invoiceItem.setDiscount(Arith.round(priceSubtotal - invoiceItem.getFloorPrice(), 2));//折扣 = 金额 - 底价
                    invoiceItem.setTax(v.getTax());
                    invoiceItem.setUom(v.getUom());
                    invoiceItem.setSourceItemId(v.getSourceItemId());
                    invoiceItem.setAmtUntaxed(v.getAmtUntaxed());
                    invoiceItem.setAmtTax(v.getAmtTax());
                    invoiceItem.setProduct(v.getProduct());
                }
            }
            invoiceItems.add(invoiceItem);
        });
        invoiceCanet.computeAmt();
        invoiceCanet.setInvoiceItems(invoiceItems);
        return invoiceCanet;
    }

    /**
     * 卡内生成营运商账单
     *
     * @param invoice
     * @param operator
     * @return
     */
    private void canetOperator(Invoice invoice, PartyRole operator) {
        Invoice operatorInvoice = new Invoice();
        operatorInvoice.setCompany(invoice.getCompany());//公司编码
        operatorInvoice.setPartner(partyRoleRepository.getOne(operator.getId()));//合作伙伴编码
        operatorInvoice.setDept(invoice.getDept());//合作伙伴编码
        operatorInvoice.setInventoryMode(invoice.getInventoryMode());//库存方式
        operatorInvoice.setContract(invoice.getContract());//合同编码
        operatorInvoice.setAccountingMethod(invoice.getAccountingMethod());//合同编码
        operatorInvoice.setSource(invoice.getSource());//来源单据号码
        operatorInvoice.setSourceBusinessType(invoice.getSourceBusinessType());//源单据号码
        operatorInvoice.setOrigin(invoice.getOrigin());//源单据号码
        operatorInvoice.setOriginBusinessType(invoice.getOriginBusinessType());//源单据类型
        operatorInvoice.setCustomerExpand(invoice.getCustomerExpand());//会员扩展信息编码
        operatorInvoice.setCreator(invoice.getCreator());//创建人
        operatorInvoice.setCreateDate(new Date());//创建日期
        operatorInvoice.setClassification(invoice.getClassification());//凭证分类
        operatorInvoice.setRemark("公司：" + invoice.getCompany().getId() + ";门店" + invoice.getDept().getName());
        Set<InvoiceItem> invoiceItems = new HashSet<>();
        invoice.getInvoiceItems().forEach(v -> {
            InvoiceItem invoiceItem = new InvoiceItem();
            JSONArray operatorProduct = invoiceltemDao.findOperatorProduct(operator.getId(), v.getProduct().getId());
            double v1 = operatorProduct.size() == 0 ? 0 : Double.valueOf(((JSONObject) operatorProduct.get(0)).get("P116").toString());
            invoiceItem.setQty(v.getQty());//数量
            invoiceItem.setPriceUnit(v.getDiscount() * v1);//单台佣金
            invoiceItem.setPriceSubtotal(invoiceItem.getPriceUnit() * invoiceItem.getQty());//金额：单台佣金 * 数量
            invoiceItem.setTax(v.getTax());
            invoiceItem.setUom(v.getUom());
            invoiceItem.setSourceItemId(v.getSourceItemId());
            invoiceItem.setAmtUntaxed(v.getAmtUntaxed());
            invoiceItem.setAmtTax(v.getAmtTax());
            invoiceItem.setProduct(v.getProduct());
            invoiceItems.add(invoiceItem);
        });
        operatorInvoice.computeAmt();
        operatorInvoice.setInvoiceItems(invoiceItems);
        invoiceRepository.saveAndFlush(operatorInvoice);
    }


    /**
     * 功能：账单冲销
     */
    public Invoice writeOff(SimpleRequest request) {
        Invoice invoice = request.getO(Invoice.class);
        invoice.checkWriteOff();
        invoice.setState(2);
        invoice = invoiceRepository.saveAndFlush(invoice);
        ledgerService.charge(invoice.getCompany(), invoice.getDept(), invoice.getPartner(), invoice.getInventoryMode(),
                invoice.getAccountingMethod(), invoice.getInnerBusinessKey(), invoice.getInnerBusinessType(),
                new Date(), "current_unsettled", -1 * invoice.getAmtTotal());
        return invoice;
    }

    /*
     * 查找未结算账单明细 : 通过合同查询
     */
    public Page<Map<String, Object>> findInvoiceItemsIsNotDoneByContractId(SimpleRequest request) {
        JSONObject requestJO = request.getJO();
        String contractId = requestJO.getString("contractId");
        Contract contract = SpringManager.getBean(ContractRepository.class).findOne(contractId);

        Date reconcileBeginDate = new Date();
        Date reconcileEndDate = new Date();

        if (requestJO.containsKey("reconcileBeginDate") && requestJO.containsKey("reconcileEndDate")) {
            try {
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                reconcileBeginDate = dateFormat.parse(requestJO.getString("reconcileBeginDate"));
                reconcileEndDate = dateFormat.parse(requestJO.getString("reconcileEndDate"));
            } catch (ParseException e) {
                throw new RuntimeException("输入的日期格式不正确，" + e.getMessage());
            }
        }
        Date newDate = DateUtil.add(reconcileEndDate, Calendar.DATE, 1);
        Page<Map<String, Object>> invoiceItemPages = invoiceItemRepository
                .findInvoiceItemsByIsNotDone(contract.getCompany().getId(), contract.getPartner().getId(), contract.getCooperation().getInventoryMode(), false,
                        reconcileBeginDate, newDate,
                        PageRequest.of(request.get("page"), request.get("size")));
        if (!invoiceItemPages.hasContent()) throw new RuntimeException("未找到待结算单据！");
        return invoiceItemPages;
    }

    /*
     * 运营商
     * 查找未结算账单明细 : 通过平台公司,合作方式,运营商
     */
    public Page<Map<String, Object>> findInvoiceItemsIsPartnerAndCompanyAndContract(SimpleRequest request) {
        JSONObject requestJO = request.getJO();
        String partnerId = request.get("partnerId");
        String companyId = request.get("companyId");
        String contractId = request.get("contractId");

        Date reconcileBeginDate = new Date();
        Date reconcileEndDate = new Date();

        if (requestJO.containsKey("reconcileBeginDate") && requestJO.containsKey("reconcileEndDate")) {
            try {
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                reconcileBeginDate = dateFormat.parse(requestJO.getString("reconcileBeginDate"));
                reconcileEndDate = dateFormat.parse(requestJO.getString("reconcileEndDate"));
            } catch (ParseException e) {
                throw new RuntimeException("输入的日期格式不正确，" + e.getMessage());
            }
        }

        Page<Map<String, Object>> invoiceItemPages = invoiceItemRepository
                .findAllByPartnerAndCompanyAndContract(false, partnerId, companyId, "distribution".equals(contractId) ? "IM_OWN" : "IM_CONSIGN", reconcileBeginDate, reconcileEndDate,
                        PageRequest.of(request.get("page"), request.get("size")));
        if (!invoiceItemPages.hasContent()) throw new RuntimeException("未找到待结算单据！");
        return invoiceItemPages;
    }

    /*
     * 查找未结算账单明细 : 通过客户编码、库存方式、采购核算方式、公司编码、部门编码
     */
    public Page<Map<String, Object>> findInvoiceItemsByLedgerUnique(SimpleRequest request) {
        String partnerId = request.get("partnerId");
        String companyId = request.get("companyId");
//        String deptId = request.get("deptId");
        String inventoryMode = request.get("inventoryMode");
        String accountingMethod = request.get("accountingMethod");
        JSONObject requestJO = request.getJO();
        Date reconcileBeginDate = new Date();
        Date reconcileEndDate = new Date();

        if (requestJO.containsKey("reconcileBeginDate") && requestJO.containsKey("reconcileEndDate")) {
            try {
                DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
                reconcileBeginDate = dateFormat.parse(requestJO.getString("reconcileBeginDate"));
                reconcileEndDate = dateFormat.parse(requestJO.getString("reconcileEndDate"));

            } catch (ParseException e) {
                // ... handle parsing exception
            }
        }

        Page<Map<String, Object>> invoiceItemPages = invoiceItemRepository.findInvoiceItemsByLedgerUnique(partnerId, companyId, inventoryMode, accountingMethod, false, reconcileBeginDate, getEndOfDay(reconcileEndDate), PageRequest.of(request.get("page"), request.get("size")));
        if (!invoiceItemPages.hasContent()) throw new RuntimeException("未找到待结算单据！");
        return invoiceItemPages;
    }

    public static Date getEndOfDay(Date date) {
        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(date.getTime()),
                ZoneId.systemDefault());
        LocalDateTime endOfDay = localDateTime.with(LocalTime.MAX);
        return Date.from(endOfDay.atZone(ZoneId.systemDefault()).toInstant());
    }

    /*
     * 增加结算表完成数量
     * */
    public void addInvoiceItemSettledQty(InvoiceItem invoiceItem, double qty) {
        invoiceItem.changeSettledQty(qty);
    }


    /*
     * 增加结算表冻结数量,更新完成状态
     * */
    public void addInvoiceItemFreezeQty(InvoiceItem invoiceItem, double qty) {
        updateInvoiceItemFreezeQty(invoiceItem, qty);
    }

    /*
     * 减少结算表冻结数量,更新完成状态
     * */
    public void decreaseInvoiceItemFreezeQty(InvoiceItem invoiceItem, double qty) {
        updateInvoiceItemFreezeQty(invoiceItem, qty);
    }

    /*
     * 更新结算表冻结数量,更新完成状态
     * */
    private void updateInvoiceItemFreezeQty(InvoiceItem invoiceItem, double qty) {
        invoiceItem.changeFreezeQty(qty);
        invoiceItem.updateDone();
    }


    /*
     * 创建结算单
     * */
    public Settle createSettle(SimpleRequest simpleRequest) {
        Invoice invoice = simpleRequest.getO(Invoice.class);
        Settle settle = new Settle();
        settle.setCompany(invoice.getCompany());
        settle.setDept(invoice.getDept());
        settle.setPartner(invoice.getPartner());
        settle.setCooperation(invoice.getContract().getCooperation());
        settle.setNote("自动生成！");
        settle.setScene(invoice.getScene());
        List<SettleItem> settleItemList = new ArrayList<>();
        invoice.getInvoiceItems().forEach(
                (invoiceItem) -> {
                    SettleItem settleItem = new SettleItem();
                    settleItem.setProduct(invoiceItem.getProduct());
                    settleItem.setUom(invoiceItem.getUom());
                    settleItem.setTax(invoiceItem.getTax());
                    settleItem.setPriceUnit(invoiceItem.getPriceUnit());
                    settleItem.setInvoiceQty(invoiceItem.getQty());
                    settleItem.setPendingQty(invoiceItem.getQty());
                    settleItem.setActualQty(invoiceItem.getQty());
                    settleItem.setInvoiceItem(invoiceItem);
                    settleItem.setOrigin(invoiceItem.getOrigin());
                    settleItem.setOriginBusinessType(invoiceItem.getOriginBusinessType());
                    settleItem.setOriginItemId(invoiceItem.getOriginItemId());
                    settleItemList.add(settleItem);
                }
        );
        settle.setSettleItems(settleItemList);
        settle.setItems(settleItemList);
        return settle;
    }

    public Invoice findBySourceBusinessTypeAndSource(String sourceBusinessType, String source) {
        return invoiceRepository.findBySourceBusinessTypeAndSource(sourceBusinessType, source);
    }


    private com.apestech.framework.json.JSONObject invoiceQuery(SimpleRequest request) throws Exception {

        JSONObject jo = request.getJO();
        JSONArray condition = jo.getJSONArray("condition");
        if (condition != null) {
            for (int i = 0; i < condition.size(); i++) {
                if ("IDS".equals(condition.getJSONObject(i).getString("field"))) {
                    if (checkLetter(condition.getJSONObject(i).getString("value"))) {
                        JSONObject jsonObject = condition.getJSONObject(i);
                        jsonObject.put("field", "SOURCE_ORIGIN");
                    }

                }

            }
            jo.put("condition", condition);
        }

        return JSON.toJSON(jo.toJSONString());
    }


    /**
     * 该方法主要使用正则表达式来判断字符串中是否包含字母
     *
     * @return 返回是否包含
     */
    public boolean checkLetter(String id) {
        if (Objects.isNull(id))
            return false;
        String regex = ".*[a-zA-Z]+.*";
        Matcher m = Pattern.compile(regex).matcher(id);
        return m.matches();
    }

    public Object node(JSONObject jo) {
        Object node = null;
        if (jo.get("node") == null) {
            JSONObject data = (JSONObject) jo.get("data");
            node = data.get("node");
        } else {
            node = jo.get("node");
        }
        return node;
    }

    public JSONObject findPriceChange(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        //取结果集
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        Date createDate = new SimpleDateFormat("yyyy-MM-dd").parse(String.valueOf(((JSONObject) node).get("CREATE_DATE").toString()));
        List<Map> resultList = invoiceRepository.findPriceChange(((JSONObject) node).get("PRODUCT_ID").toString(),
                ((JSONObject) node).get("COMPANY_ID").toString(), createDate, inventory);
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    public JSONObject findPriceChangeMonth(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        //取结果集
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        List<Map> resultList = invoiceRepository.findPriceChangeMonth(((JSONObject) node).get("PRODUCT_ID").toString(),
                ((JSONObject) node).get("COMPANY_ID").toString(), ((JSONObject) node).get("CREATE_DATE").toString(), inventory);
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    public JSONObject findBalance(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        //取结果集
        Date createDate = new SimpleDateFormat("yyyy-MM-dd").parse(String.valueOf(((JSONObject) node).get("CREATE_DATE").toString()));
        List<Map> resultList = invoiceRepository.findBalance(((JSONObject) node).get("PRODUCT_ID").toString(), inventory,
                ((JSONObject) node).get("COMPANY_ID").toString(), createDate);
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    public JSONObject findBalanceMonth(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        //取结果集
        List<Map> resultList = invoiceRepository.findBalanceMonth(((JSONObject) node).get("PRODUCT_ID").toString(), inventory,
                ((JSONObject) node).get("COMPANY_ID").toString(), ((JSONObject) node).get("CREATE_DATE").toString());
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    public JSONObject findBeginning(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        //取结果集
        Date createDate = new SimpleDateFormat("yyyy-MM-dd").parse(String.valueOf(((JSONObject) node).get("CREATE_DATE").toString()));
        List<Map> resultList = invoiceRepository.findBeginning(((JSONObject) node).get("PRODUCT_ID").toString(), inventory,
                ((JSONObject) node).get("COMPANY_ID").toString(), createDate);
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    public JSONObject findBeginningMonth(SimpleRequest request) throws ParseException {
        JSONObject jo = request.getJO();
        Object node = node(jo);
        String inventory;
        if (((JSONObject) node).get("COOPERATION").toString().equals("代销")) {
            inventory = "consignment";
        } else {
            inventory = "distribution";
        }
        //取结果集
        List<Map> resultList = invoiceRepository.findBeginningMonth(((JSONObject) node).get("PRODUCT_ID").toString(), inventory,
                ((JSONObject) node).get("COMPANY_ID").toString(), ((JSONObject) node).get("CREATE_DATE").toString());
        JSONObject object = new JSONObject();
        object.put("content", resultList);
        object.put("total", resultList.size());
        return object;
    }

    private void operatorProcess(Invoice invoice) {
        try {
            supplierInvoice(invoice);
            customerInvoice(invoice);
        } catch (Exception e) {
            e.printStackTrace();
            throw new RuntimeException("运营商账单生成异常：[" + e.getMessage() + "]");
        }
    }

    public void supplierInvoice(Invoice invoice) {
        // 判断是否是供应商账单
        if (invoice.getPartner() == null || invoice.getPartner().getRole() == null || !invoice.getPartner().getRole().getId().equals("Supplier"))
            return;
        if (invoice.getOriginBusinessType().equals("PriceChange")) return; // 排除来源是采购变价单的数据
        // 获取运营商
        PartyRole operator = getOperatorByParty(invoice.getCompany().getParty());
        // 循环遍历明细，调用策略查询是否有供应商底价
        Set<InvoiceItem> operatorInvoiceItems = new HashSet(); // 运营商账单明细
        invoice.getInvoiceItems().stream().forEach(invoiceItem -> {
            Product product = invoiceItem.getProduct();
            //调用策略JS01
            JSONObject jo = conditionExecByJS01(invoice, invoiceItem, operator, invoice.getPartner());
            // 若存在运营商底价则生成运营商账单明细
            double p102 = jo.getDoubleValue("P102");  // 供应商结算底价
            double p103 = jo.getDoubleValue("P103");  // 运营商结算底价
            if (p102 != 0 && invoiceItem.getPriceUnit() - p102 > 0) {
                invoiceItem.setFloorPrice(p102);
                invoiceItem.setDiscount(Arith.mul(Arith.sub(invoiceItem.getPriceUnit(), p102), invoiceItem.getQty()));
            } else {
                invoiceItem.setFloorPrice(invoiceItem.getPriceUnit());
                invoiceItem.setDiscount(Arith.mul(Arith.sub(invoiceItem.getPriceUnit(), invoiceItem.getFloorPrice()), invoiceItem.getQty()));
            }
            if (p103 != 0 && invoiceItem.getPriceUnit() - p103 > 0) {
                operatorInvoiceItems.add(createOperatorInvoiceItem(invoiceItem, p103));
            }
        });
        if (!invoice.getCompany().getParty().getCorporate().getOperation().getId().equals("001073"))
            return;  // 供应商账单的公司的运营模式为赋能
        createOperatorInvoice(operatorInvoiceItems, invoice, operator);
    }

    public void customerInvoice(Invoice invoice) {
        // 判断是否是客户账单
        if (invoice.getPartner() == null || invoice.getPartner().getRole() == null || !"Customer".equals(invoice.getPartner().getRole().getId()))
            return;
        if (invoice.getPartner().getParty().getCorporate() == null
                || invoice.getPartner().getParty().getCorporate().getOperation() == null
                || !"001073".equals(invoice.getPartner().getParty().getCorporate().getOperation().getId()))
            return;  // 客户账单的合作伙伴公司的运营模式为赋能
        // 获取运营商
        PartyRole operator = getOperatorByParty(invoice.getPartner().getParty());
        // 循环遍历明细，调用策略查询是否有供应商底价
        Set<InvoiceItem> operatorInvoiceItems = new HashSet(); // 运营商账单明细
        invoice.getInvoiceItems().stream().forEach(invoiceItem -> {
            List<InventoryBatch> inventoryBatchs = getInventoryBatchByInvoice(invoice, invoiceItem.getProduct());
            if (inventoryBatchs == null) return;
            double p102 = 0;
            double p103 = 0;
            for (InventoryBatch inventoryBatch : inventoryBatchs) {
                //调用策略JS01
                JSONObject jo = conditionExecByJS01(invoice, invoiceItem, operator, inventoryBatch.getSupplier());
                // 若存在运营商底价则生成运营商账单明细
                p102 += jo.getDoubleValue("P102");  // 供应商结算底价
                p103 += jo.getDoubleValue("P103");  // 运营商结算底价
            }
            if (p103 != 0 && invoiceItem.getPriceUnit() - p103 > 0) {
                operatorInvoiceItems.add(createOperatorInvoiceItem(invoiceItem, p103));
            }
        });
        createOperatorInvoice(operatorInvoiceItems, invoice, operator);
    }

    private PartyRole getOperatorByParty(Party party) {
        PartyRole operator = partyRoleRepository.findAll(JpaDsl.toCriteria(
                        "role.id", "EQ", "Operator",
                        "party.id", "EQ", party.getId()))
                .stream().findFirst().orElse(null); // 运营商
        if (operator != null) return operator;
        PartyRole belongOperator = party.getBelongOperator();
        return belongOperator;
    }

    private void createOperatorInvoice(Set<InvoiceItem> operatorInvoiceItems, Invoice invoice, PartyRole operator) {
        if (operatorInvoiceItems.isEmpty()) return;
        if (invoice.getOriginBusinessType().equals("PriceChange")) return; // 排除来源是采购变价单的数据
        if (operator == null) return;
        Invoice operatorInvoice = new Invoice();
        ReflectUtil.copyAll(invoice, operatorInvoice, "id");

        if (invoice.getPartner().getRole().getId().equals("Supplier")) {
            operatorInvoice.setCompany(invoke("fn.contract.findCompanyByPartner", getCompanyByParty(invoice.getCompany().getParty())));
        }
        if (invoice.getPartner().getRole().getId().equals("Customer")) {
            operatorInvoice.setCompany(invoke("fn.contract.findCompanyByPartner", getCompanyByParty(invoice.getPartner().getParty())));
        }
        operatorInvoice.setPartner(operator);
        operatorInvoice.setInventoryMode(InventoryMode.IM_OWN);
        operatorInvoice.setInvoiceItems(operatorInvoiceItems);
        operatorInvoice.computeAmt();

        invoiceRepository.saveAndFlush(operatorInvoice);
        ledgerService.charge(operatorInvoice.getCompany(), operatorInvoice.getDept(), operatorInvoice.getPartner(), operatorInvoice.getInventoryMode(),
                operatorInvoice.getAccountingMethod(), operatorInvoice.getInnerBusinessKey(), operatorInvoice.getInnerBusinessType(),
                operatorInvoice.getDateAccount(), "current_unsettled", operatorInvoice.getAmtTotal());
    }

    private InvoiceItem createOperatorInvoiceItem(InvoiceItem invoiceItem, double p103) {
        InvoiceItem operatorInvoiceItem = new InvoiceItem();
        ReflectUtil.copyAll(invoiceItem, operatorInvoiceItem, "id");
        operatorInvoiceItem.setPriceUnit(Arith.sub(invoiceItem.getPriceUnit(), p103));
        operatorInvoiceItem.setDiscount(0.0);
        operatorInvoiceItem.setTax(SpringManager.getBean(TaxRepository.class).findOne(5L));
        operatorInvoiceItem.setFloorPrice(p103);
        operatorInvoiceItem.setPriceSubtotal(Arith.mul(operatorInvoiceItem.getPriceUnit(), operatorInvoiceItem.getQty()));
        PartyRole company = null;
        if (invoiceItem.getInvoice().getPartner().getRole().getId().equals("Supplier")) {
            company = invoiceItem.getInvoice().getCompany();
        }
        if (invoiceItem.getInvoice().getPartner().getRole().getId().equals("Customer")) {
            company = partyRoleRepository.findAll(JpaDsl.toCriteriaByEq("role.id", "Company",
                    "party.id", invoiceItem.getInvoice().getPartner().getParty().getId())).stream().findFirst().orElse(null);
        }
        operatorInvoiceItem.setRemark(invoiceItem.getPriceUnit() + ";公司:" + (company == null ? "无" : company.getId()) + ";门店:" + getDeptNameByCompany(company));
        return operatorInvoiceItem;
    }

    private String getDeptNameByCompany(PartyRole company) {
        Dept dept = deptRepository.findAll(JpaDsl.toCriteriaByEq("company.id", company.getId(), "deptType.code", "BC_sale")).stream().findFirst().orElse(null);
        return dept == null ? "无" : dept.getName();
    }

    private JSONObject conditionExecByJS01(Invoice invoice, InvoiceItem invoiceItem, PartyRole operator, PartyRole supplier) {
        JSONObject conditionRequest = new JSONObject();
        if (operator != null) conditionRequest.put("OPERATOR_ID", operator.getId());
        conditionRequest.put("SUPPLIER_ID", supplier.getId());
        conditionRequest.put("PRODUCT_ID", invoiceItem.getProduct().getId());
        conditionRequest.put("conditionStrategyId", "JS01");
        conditionRequest.put("SYS_RQ", new Date());
        return JSONObject.parseObject(JSONObject.toJSONString(invoke("condition.execConditionStrategy", conditionRequest)));
    }

    public List<InventoryBatch> getInventoryBatchByInvoice(Invoice invoice, Product product) {
        String source = invoice.getSource();
        Voucher voucher = SpringManager.getBean(VoucherRepository.class).findOne(source);
        if (voucher == null) return new ArrayList<>();
        VoucherItem voucherItem = voucher.getVoucherItems().stream().filter(item -> item.getProduct().getId().equals(product.getId())).findFirst().orElse(null);
        if (voucherItem == null) return new ArrayList<>();
        List<VoucherInventory> voucherInventories = SpringManager.getBean(VoucherInventoryRepository.class).findByVoucherItem(voucherItem);
        return voucherInventories.stream().map(voucherInventory -> voucherInventory.getInventoryBatch()).collect(Collectors.toList());
    }

    public PartyRole getCompanyByParty(Party party) {
        return partyRoleRepository.findAll(JpaDsl.toCriteriaByEq("party.id", party.getId(), "role.id", "Company")).stream().findFirst().get();
    }

    /**
     * 定时生成供应商收入
     *
     * @param request
     */
    public void timing(SimpleRequest request) {
        //获取日期
        JSONObject jsonObject = request.getJO();

        String date = jsonObject.getString("taskDate") == null ? new SimpleDateFormat("yyyy-MM").format(new Date()) : jsonObject.get("taskDate").toString();

        //查询是否生成过供应商收入单
        List<Map> income = invoiceltemDao.findIncome(date);
        if (income.size() > 0) return;

        List<Map> invoices = invoiceltemDao.findCreateDate(date);
        invoices.forEach(v -> {

            //获取系统参数表
            List<Map> businessParam;
            if ("1001".equals(v.get("COMPANY_ID"))) {
                businessParam = invoiceltemDao.busines("38");
            } else {
                businessParam = invoiceltemDao.busines("39");
            }
            saveIncome(request, v, businessParam);//生成供应商收入单
        });
    }

    private void saveIncome(SimpleRequest request, Map v, List<Map> businessParam) {
        JSONObject jsonObject = request.getJO();
        // 明细数据
        Map map = businessParam.get(0);
        Calendar c = Calendar.getInstance();
        Calendar ca = Calendar.getInstance();
        if (jsonObject.getString("taskDate") == null) {
            c.add(Calendar.MONTH, -1);//回去本月去掉
            c.set(Calendar.DAY_OF_MONTH, c.getActualMinimum(Calendar.DAY_OF_MONTH)); //获取上个月的第一天

            ca.add(Calendar.MONTH, -1);//回去本月去掉
            ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH)); //获取当前月的最后一天
        } else {
            c.set(Calendar.DAY_OF_MONTH, ca.getActualMinimum(Calendar.DAY_OF_MONTH));//获取当前月的第一天
            ca.set(Calendar.DAY_OF_MONTH, ca.getActualMaximum(Calendar.DAY_OF_MONTH));//获取当前月的最后一天
        }


        IncomeItem incomeItem = new IncomeItem();
        incomeItem.setIncomeAmount(Double.valueOf(v.get("DISCOUNT").toString()));
        incomeItem.setIncomeType(SpringManager.getBean(IncomeTypeRepository.class).findOne(map.get("VALUE2").toString()));
        incomeItem.setTax(SpringManager.getBean(TaxRepository.class).findOne(Long.valueOf(v.get("TAX_ID").toString())));
        incomeItem.setStartDate(c.getTime());
        incomeItem.setEndDate(ca.getTime());

        Set<IncomeItem> incomeItems = new HashSet<>();
        incomeItems.add(incomeItem);

        Income income = new Income();
        income.setCompany(SpringManager.getBean(PartyRoleRepository.class).findOne(v.get("COMPANY_ID").toString()));
        income.setPartner(SpringManager.getBean(PartyRoleRepository.class).findOne(v.get("PARTNER_ID").toString()));
        income.setDept(deptRepository.findDept(v.get("DEPT_ID").toString()));
        income.setNote("供应商账单定时生成");
        income.setAmount(Double.valueOf(v.get("DISCOUNT").toString()));
        income.setCreator(request.getPersonId());
        income.setCooperation("IM_OWN".equals(v.get("INVENTORY_MODE")) ? cooperationRepository.getOne("distribution") : cooperationRepository.getOne("consignment"));
        income.setInComeCategory("1001".equals(income.getCompany()) ? enumerationRepository.getOne("rebate") : enumerationRepository.getOne("cost"));
        income.setState("sent");
        income.setIncomeItems(incomeItems);
        income.setCreator(request.getPerson());
        SpringManager.getBean(IncomeRepository.class).saveAndFlush(income);

        invoke("income.supplier.approve", income);//费用账单自动审核
    }

    public com.apestech.framework.json.JSONObject operatorQuery(SimpleRequest request) throws Exception {
        JSONObject jo = request.getJO();
        String postId = request.getPostId();
        JSONObject field = new JSONObject();
        field.put("field", "POST_ID");
        field.put("operator", "EQ");
        field.put("value", postId);

        JSONArray condition = jo.getJSONArray("condition");
        condition.add(field);
        jo.put("condition", condition);
        return com.apestech.framework.json.JSONObject.toJSON(com.apestech.framework.json.JSONObject.toJSONString(jo));
    }

    public com.apestech.framework.json.JSONObject querySummary(SimpleRequest request) throws Exception {
        JSONObject jo = request.getJO();
        JSONArray condition = jo.getJSONArray("condition");
        String format = new SimpleDateFormat("yyyy-MM-dd").format(new Date());
        String date = ((JSONObject) condition.get(1)).get("value").toString().substring(0, 10);
        if (condition != null) {
            for (int i = 0; i < condition.size(); i++) {
                if ("CREATE_DATE".equals(condition.getJSONObject(i).getString("field"))) {
                    JSONObject json = new JSONObject();
                    if ("GTE".equals(condition.getJSONObject(i).getString("operator"))) {
                        jo.put("CREATE_DATE_START", com.apestech.framework.util.DateUtil.strToDate(condition.getJSONObject(i).getString("value")));
                    } else if ("LTE".equals(condition.getJSONObject(i).getString("operator"))) {
                        json.put("field", "CREATE_DATE_END");
                        json.put("operator", "EQ");
                        json.put("value", condition.getJSONObject(i).getString("value"));
                        json.put("type", "date");
                        condition.add(json);
                    }
                }
            }
            jo.put("condition", condition);
        }
        return new com.apestech.framework.json.JSONObject(jo);
    }

    public com.apestech.framework.json.JSONObject query(SimpleRequest request) throws Exception {
        JSONObject jo = request.getJO();
        JSONArray condition = jo.getJSONArray("condition");
        if (condition != null) {
            for (int i = 0; i < condition.size(); i++) {
                if ("CREATE_DATE_DATE1".equals(condition.getJSONObject(i).getString("field"))) {
                    JSONObject json = new JSONObject();
                    if ("GTE".equals(condition.getJSONObject(i).getString("operator"))) {
                        json.put("field", "CREATE_DATE_START");
                        json.put("operator", "EQ");
                        json.put("value", condition.getJSONObject(i).getString("value"));
                        json.put("type", "date");
                        condition.add(json);
                    } else if ("LTE".equals(condition.getJSONObject(i).getString("operator"))) {
                        json.put("field", "CREATE_DATE_END");
                        json.put("operator", "EQ");
                        json.put("value", condition.getJSONObject(i).getString("value"));
                        json.put("type", "date");
                        condition.add(json);
                    }
                }
            }
            jo.put("condition", condition);
        }
        return com.apestech.framework.json.JSON.toJSON(jo.toJSONString());
    }

    /**
     * 查询账单
     *
     * @param request
     * @return
     */
    public List findOneToList(SimpleRequest request) {
        JSONObject jo = request.getJO();
        if (!jo.containsKey("transactionDate") || !jo.containsKey("supplier")) throw new RuntimeException("有必填项未填写");
        if (jo.get("transactionDate") == null || jo.get("supplier") == null) throw new RuntimeException("有必填项未填写");
        //供应商
        String supplier = jo.getJSONObject("supplier").getString("id");
        //账单日期
        JSONArray transactionDate = jo.getJSONArray("transactionDate");
        //账单日期起
        Date transactionDateStart = transactionDate.getDate(0);
        //账单日期止
        Date transactionDateEnd = DateUtil.add(transactionDate.getDate(1), Calendar.DATE, 1);
        //创建日期起
        String createDateStartSql = " AND 1 = 1  ";
        //创建日期止
        String createDateEndSql = " AND 1 = 1  ";
        if (jo.containsKey("createDate") && jo.getJSONArray("createDate") != null) {
            //创建日期
            JSONArray createDate = jo.getJSONArray("createDate");
            //创建日期起
            Date createDateStart = createDate.getDate(0);
            //创建日期止
            Date createDateEnd = DateUtil.add(createDate.getDate(1), Calendar.DATE, 1);
            //创建日期起
            createDateStartSql = " AND A.CREATE_DATE >= to_date('" + DateUtil.format(createDateStart, DateUtil.SHOW_DATE_FORMAT) + "','yyyy-MM-dd') ";
            //创建日期止
            createDateEndSql = " AND A.CREATE_DATE < to_date('" + DateUtil.format(createDateEnd, DateUtil.SHOW_DATE_FORMAT) + "','yyyy-MM-dd') ";
        }

        //判断该供应商是否有未确认的对账单
        List<Map> platform = invoiceRepository.findPlatform(transactionDateStart, transactionDateEnd, supplier);
        if (!platform.isEmpty())
            throw new RuntimeException("该供应商存在未确认的对账单:" + platform.get(0).get("id") + " 等" + platform.size() + "条,不允许调整账单日期！");
        //供应商
        String supplierId = " AND A.PARTNER_ID = '" + supplier + "'";
        //账单日期起
        String transactionDateStartSql = " AND A.THIRD_TIME >= to_date('" + DateUtil.format(transactionDateStart, DateUtil.SHOW_DATE_FORMAT) + "','yyyy-MM-dd') ";
        //账单日期止
        String transactionDateEndSql = " AND A.THIRD_TIME < to_date('" + DateUtil.format(transactionDateEnd, DateUtil.SHOW_DATE_FORMAT) + "','yyyy-MM-dd') ";
        //单号或源单号
        String id = jo.containsKey("id") && jo.getString("id") != null ? " AND (A.ID LIKE '" + jo.getString("id") + "' OR A.ORIGIN = '" + jo.getString("id") + "')" : " AND 1 = 1 ";
        //公司
        String company = jo.containsKey("company") && jo.getJSONObject("company") != null ? " AND A.COMPANY_ID = '" + jo.getJSONObject("company").getString("id") + "'" : " AND 1 = 1 ";
        //源单据
        String originBusinessType = jo.containsKey("originBusinessType") && jo.getJSONObject("originBusinessType") != null ? " AND A.ORIGIN_BUSINESS_TYPE = '" + jo.getJSONObject("originBusinessType").getString("id") + "'" : " AND 1 = 1 ";
        StringBuffer sSql = new StringBuffer();
        sSql.append("SELECT DISTINCT A.* " +
                "  FROM ACC_INVOICE A, ACC_INVOICE_ITEM B " +
                " WHERE A.ID = B.INVOICE_ID " +
                " AND B.SETTLED_QTY = 0 " +
                supplierId + transactionDateStartSql + transactionDateEndSql + id + company + originBusinessType + createDateStartSql + createDateEndSql +
                " ORDER BY A.THIRD_TIME,A.ID ");
        Query query = entityManager.createNativeQuery(sSql.toString());
        query.unwrap(NativeQueryImpl.class).setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP);
        if (query.getResultList().size() == 0) return new ArrayList();
        List<Map> resultList = query.getResultList();
        List<Map> collect = resultList.stream().map(item -> {
            String carLicense = "";
            if (item.get("CUSTOMER_EXPAND_ID") != null)
                carLicense = this.invoke("fn.fnCustomerExpandService.accordIdFindCarLicense", MapUtil.mapper("id", item.get("CUSTOMER_EXPAND_ID")));
            return MapUtil.mapper("createDate", DateUtil.format(DateUtil.parse(item.get("CREATE_DATE").toString(), DateUtil.SHOW_DATE_FORMAT), DateUtil.SHOW_DATE_FORMAT),
                    "thirdTime", DateUtil.format(DateUtil.parse(item.get("THIRD_TIME").toString(), DateUtil.SHOW_DATE_FORMAT), DateUtil.SHOW_DATE_FORMAT),
                    "id", item.get("ID"), "origin", item.get("ORIGIN"), "amtTotal", item.get("AMT_TOTAL"), "carLicense", carLicense, "thirdId", item.get("THIRD_ID"), "company", partyRoleRepository.findOne(item.get("COMPANY_ID").toString()));
        }).collect(Collectors.toList());
        return collect;
    }

    /**
     * 修改账单日期
     *
     * @param request
     * @return
     */
    public JSONObject changeSave(SimpleRequest request) {
        JSONArray invoices = request.getJO().getJSONArray("invoices");
        //只取勾选行
        List<Object> list = invoices.stream().filter(invoiceObj -> {
            JSONObject jsonObject = (JSONObject) invoiceObj;
            if (!jsonObject.containsKey("selected")) throw new RuntimeException("请勾选需要修改的账单");
            return jsonObject.getBoolean("selected");
        }).collect(Collectors.toList());
        if (list.isEmpty()) throw new RuntimeException("请勾选需要修改的账单");
        list.forEach(invoiceObj -> {
            JSONObject jsonObject = (JSONObject) invoiceObj;
            Invoice invoice = invoiceRepository.findOne(jsonObject.getLong("id"));
            //修改账单日期
            if (jsonObject.getDate("newThirdTime") == null) throw new RuntimeException("请先选择新帐单日期");
            invoice.setThirdTime(jsonObject.getDate("newThirdTime"));
            //保存修改人
            invoice.setModifiedUid(request.getPerson());
            //保存修改日期
            invoice.setModifiedDate(new Date());
            invoiceRepository.saveAndFlush(invoice);
        });
        return request.getJO();
    }
}
